/* CsJni - .NET / Java Bridge
 * 
 * Copyright 2010 Robert E. Burke Licensed under the Apache License, Version 
 * 2.0 (the "License"); you may not use this file except in compliance with 
 * the License. You may obtain a copy of the License at 
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable 
 * law or agreed to in writing, software distributed under the License is 
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
 * KIND, either express or implied. See the License for the specific language 
 * governing permissions and limitations under the License. 
*/

using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Xml;
using System.IO;
using CsJni.PlatformAbstraction;

namespace CsJni
{
    internal sealed class JavaVMConfigurationSection : ConfigurationSection
    {
        private JavaVMConfiguration mCfg;

        public JavaVMConfigurationSection()
        {
        }

        internal JavaVMConfiguration Configuration
        {
            get { return mCfg; }
        }

        protected override void DeserializeSection(XmlReader reader)
        {
            try
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(reader);
                mCfg = CreateFromXml(doc.DocumentElement);
            }
            catch (Exception ex)
            {
                throw new JniException(JniResourceManager.Instance.GetString("jvmConfigParseError"), ex);
            }
        }

        internal static JavaVMConfiguration CreateFromXml(XmlNode section)
        {
            // TODO - add dtd or schema validation?
            JavaVMConfiguration configuration = JavaVMConfiguration.CreateDefault();

            configuration.Version = (JniVersion)Enum.Parse(typeof(JniVersion),
                GetAttribute(section, "jniversion", JniVersion.Version1Minor4.ToString()));
            configuration.IgnoreUnrecognized = Boolean.Parse(
                GetAttribute(section, "ignoreUnrecognizedOptions", Boolean.FalseString));

            string loggingTypes = GetAttribute(section, "logTypes", "none");
            configuration.JavaVMLogTypes = (JavaVMLogTypes)Enum.Parse(typeof(JavaVMLogTypes), loggingTypes, true);

            ParsePlatform(section, configuration);

            XmlNodeList kids = section.SelectSingleNode("options").ChildNodes;
            foreach (XmlNode kid in kids)
            {
                if (kid.NodeType != XmlNodeType.Element)
                {
                    continue;
                }
                if (kid.Name == "java.class.path")
                {
                    configuration.AddClassPathPart(GetNodeValue(kid, string.Empty));
                }
                else if (kid.Name == "java.library.path")
                {
                    configuration.AddLibraryPathPart(GetNodeValue(kid, string.Empty));
                }
                else if (kid.Name == "java.option")
                {
                    string option = GetNodeValue(kid, string.Empty).Trim();
                    if (option.Length == 0)
                    {
                        throw new JniException(JniResourceManager.Instance.GetString("jvmCfgEmptyOption"));
                    }
                    configuration.AddOption(option);
                }
                else
                {
                    throw new JniException(JniResourceManager.Instance.GetString("jvmCfgInvalidNode") + kid.Name);
                }
            }

            ParseCounterparts(section, configuration);

            return configuration;
        }

        private static void ParseCounterparts(XmlNode section, JavaVMConfiguration configuration)
        {
            XmlNodeList kids = section.SelectSingleNode("javaToNative").ChildNodes;
            foreach (XmlNode kid in kids)
            {
                if (kid.NodeType != XmlNodeType.Element)
                {
                    continue;
                }
                if (kid.Name == "counterpartAssembly")
                {
                    configuration.AddCounterpartPathPart(GetNodeValue(kid, string.Empty));
                }
                else
                {
                    throw new JniException(JniResourceManager.Instance.GetString("jvmCfgInvalidNode") + kid.Name);
                }
            }
        }

        private static void ParsePlatform(XmlNode section, JavaVMConfiguration configuration)
        {
            JniPlatformId platformId = PlatformFactory.PlatformId;
            XmlNode platformNode = section.SelectSingleNode("platform[@id=\'" + platformId.ToString() + "\']");
            configuration.CrtLib = GetAttribute(platformNode, "crtLib", configuration.CrtLib);

            XmlNodeList kids = platformNode.ChildNodes;
            foreach (XmlNode kid in kids)
            {
                if (kid.NodeType != XmlNodeType.Element)
                {
                    continue;
                }
                if (kid.Name == "vmDiscovery")
                {
                    JavaVMDiscoveryType discoveryType = (JavaVMDiscoveryType)Enum.Parse(typeof(JavaVMDiscoveryType),
                        GetAttribute(kid, "type", JavaVMDiscoveryType.Path.ToString()));
                    string discoverySpec = GetNodeValue(kid, string.Empty);
                    JavaVMDiscoveryStrategy strategy = new JavaVMDiscoveryStrategy(discoveryType, discoverySpec, null);
                    configuration.AddDiscoveryStrategy(strategy);
                }
                else
                {
                    throw new JniException(JniResourceManager.Instance.GetString("jvmCfgInvalidNode") + kid.Name);
                }
            }
        }

        private static string GetNodeValue(XmlNode node, string defaultValue)
        {
            XmlNodeList kids = node.ChildNodes;
            string result = string.Empty;
            foreach (XmlNode kid in kids)
            {
                if (kid.NodeType == XmlNodeType.Text)
                {
                    result = kid.Value;
                    break;
                }
            }
            if (result.Length == 0)
            {
                result = defaultValue;
            }
            return Environment.ExpandEnvironmentVariables(result);
        }

        private static string GetAttribute(XmlNode node, string attName, string defaultValue)
        {
            string tempValue = ((XmlElement)node).GetAttribute(attName);
            if (tempValue.Length == 0)
            {
                tempValue = defaultValue;
            }
            return Environment.ExpandEnvironmentVariables(tempValue);
        }
    }
}
